﻿using System;
using System.Collections.Generic;
using System.Linq;
using GateShift.Common.DataPackets.Chat;
using GateShift.Services.ChatSystem.Exceptions;
using GateShift.Services.SDK.Services;

namespace GateShift.Services.ChatSystem.Helpers
{
    internal class ChatChannelHandler
    {
        private readonly Dictionary<Int16, ChatChannel> _channels;
        private readonly Queue<Int16> _freeChannels;


        public ChatChannelHandler()
        {
            _channels = new Dictionary<Int16, ChatChannel>();
            _freeChannels = new Queue<Int16>();
        }

        /// <summary>
        /// Creates a new chat channel
        /// </summary>
        /// <param name="channelName">The name of the channel</param>
        /// <param name="ownerID">The owner of the channel</param>
        /// <exception cref="MaximumChatChannelCountException">Thrown if the system cannot handle more channels</exception>
        public void CreateChannel(String channelName, Guid ownerID)
        {
            if (_channels.Count >= Int16.MaxValue)
                throw new MaximumChatChannelCountException("Chat channels are at max");

            ChatChannel chatChannel = new ChatChannel(ownerID, channelName);
            Int16 channelID = (Int16)_channels.Count;

            if (_freeChannels.Count > 0)
                channelID = _freeChannels.Dequeue();

            _channels.Add(channelID, chatChannel);
        }

        /// <summary>
        /// Deletes the specified channel if the owner has the permission to do so
        /// </summary>
        /// <param name="channelID">ID of the channel</param>
        /// <param name="ownerID">Owner of the channel. Use NULL for System deletes only.</param>
        /// <exception cref="KeyNotFoundException">Could not find the channelID</exception>
        /// <exception cref="SecurityChatChannelException">OwnerID is not the owner of the channel</exception>
        public void DeleteChannel(Int16 channelID, Guid? ownerID)
        {
            if (!_channels.ContainsKey(channelID))
                throw new KeyNotFoundException("Channel does not exist");

            ChatChannel chatChannel = _channels[channelID];

            if (ownerID.HasValue && chatChannel.CanDelete(ownerID.Value))
                throw new SecurityChatChannelException("Specified user is cannot delete the channel");

            //Clean any current members out
            chatChannel.ClearAllMembers();

            _channels.Remove(channelID);
            _freeChannels.Enqueue(channelID);
        }

        /// <summary>
        /// Returns a list of Chat Channel IDs for a given user
        /// </summary>
        /// <param name="userID">UserID to retrieve channels for</param>
        /// <returns>List of Channel IDs</returns>
        public List<Int16> GetChannelsForUser(Guid userID)
        {
            //Yea for LINQ!
            return (from channel in _channels where channel.Value.IsMember(userID) select channel.Key).ToList();
        }


        /// <summary>
        /// Handles data for creating a chat channel
        /// </summary>
        /// <param name="session">Session making the request</param>
        /// <param name="packet">CreateChatChannelPacket with information from client</param>
        public void HandleCreateChatChannel(IClientSession session, CreateChatChannelPacket packet)
        {

            if (String.IsNullOrEmpty(packet.ChannelName))
            {
                //TODO: Reply with InvalidName response
                return;
            }

            try
            {
                CreateChannel(packet.ChannelName, session.SessionID);
            }
            catch (MaximumChatChannelCountException)
            {
                //TODO: Reply with MaxChannels response
                return;
            }

            //TODO: Reply with ChannelID Joined Response
        }

        /// <summary>
        /// Handles data for deleting a chat channel
        /// </summary>
        /// <param name="session">Session making the request</param>
        /// <param name="packet">BasicChatChannelPacket with information from client</param>
        public void HandleDeleteChatChannel(IClientSession session, BasicChatChannelPacket packet)
        {
            if (!_channels.ContainsKey(packet.ChannelID))
            {
                //TODO: Reply with NotFound response
                return;
            }

            if (!_channels[packet.ChannelID].CanDelete(session.SessionID))
            {
                //TODO: Reply with PermissionDenied response
                return;
            }

            DeleteChannel(packet.ChannelID, session.SessionID);
            //TODO: Reply with ChannelID Left Response
        }

        /// <summary>
        /// Handles data for joining a chat channel
        /// </summary>
        /// <param name="session">Session making the request</param>
        /// <param name="packet">PasswordChatChannelPacket with information from client</param>
        public void HandleJoinChatChannel(IClientSession session, PasswordChatChannelPacket packet)
        {

            if (!_channels.ContainsKey(packet.ChannelID))
            {
                //TODO: Reply with NotFound response
                return;
            }

            if (!_channels[packet.ChannelID].CanJoin(session.SessionID))
            {
                //TODO: Reply with PermissionDenied response
                return;
            }

            try
            {
                _channels[packet.ChannelID].JoinChannel(session.SessionID, packet.Password);
            }
            catch (PasswordChatChannelException)
            {
                //TODO: Reply with PasswordIncorrect response
                return;
            }

            //TODO: Reply with ChannelID Joined Response
        }

        /// <summary>
        /// Handles data for leaving a chat channel
        /// </summary>
        /// <param name="session">Session making the request</param>
        /// <param name="packet">BasicChatChannelPacket with information from client</param>
        public void HandleLeaveChatChannel(IClientSession session, BasicChatChannelPacket packet)
        {

            if (!_channels.ContainsKey(packet.ChannelID))
            {
                //TODO: Reply with NotFound response
                return;
            }

            if (_channels[packet.ChannelID].IsOwner(session.SessionID))
            {
                HandleDeleteChatChannel(session, packet);
                return;
            }

            _channels[packet.ChannelID].LeaveChannel(session.SessionID);
            //TODO: Reply with ChannelID Left Response
        }

        /// <summary>
        /// Handles data for banning a member of a chat channel
        /// </summary>
        /// <param name="session">Session making the request</param>
        /// <param name="packet">PlayerChatChannelPacket with information from client</param>
        public void HandleBanChatChannel(IClientSession session, PlayerChatChannelPacket packet)
        {

            if (!_channels.ContainsKey(packet.ChannelID))
            {
                //TODO: Reply with NotFound response
                return;
            }

            if (!_channels[packet.ChannelID].CanModifyBan(session.SessionID))
            {
                //TODO: Reply with PermissionDenied response
                return;
            }

            //TODO Get UserID
            //TODO Verify user is not the member/owner

            _channels[packet.ChannelID].BanMember(Guid.Empty, session.SessionID);

            //TODO Send kick response to banned member
            //TODO Send success response to moderator
        }

        /// <summary>
        /// Handles data for unbanning a member of a chat channel
        /// </summary>
        /// <param name="session">Session making the request</param>
        /// <param name="packet">PlayerChatChannelPacket with information from client</param>
        public void HandleUnBanChatChannel(IClientSession session, PlayerChatChannelPacket packet)
        {

            if (!_channels.ContainsKey(packet.ChannelID))
            {
                //TODO: Reply with NotFound response
                return;
            }

            if (!_channels[packet.ChannelID].CanModifyBan(session.SessionID))
            {
                //TODO: Reply with PermissionDenied response
                return;
            }

            //TODO Get UserID

            _channels[packet.ChannelID].UnBanMember(Guid.Empty, session.SessionID);

            //TODO Send success response to moderator
        }

        /// <summary>
        /// Handles data for promoting a moderator of a chat channel
        /// </summary>
        /// <param name="session">Session making the request</param>
        /// <param name="packet">PlayerChatChannelPacket with information from client</param>
        public void HandlePromoteModeratorChatChannel(IClientSession session, PlayerChatChannelPacket packet)
        {

            if (!_channels.ContainsKey(packet.ChannelID))
            {
                //TODO: Reply with NotFound response
                return;
            }

            if (!_channels[packet.ChannelID].CanModifyModerator(session.SessionID))
            {
                //TODO: Reply with PermissionDenied response
                return;
            }

            //TODO Get UserID
            //TODO Verify user is not the user/owner

            _channels[packet.ChannelID].AddModerator(Guid.Empty, session.SessionID);

            //TODO Send kick response to banned member
            //TODO Send success response to moderator
        }

        /// <summary>
        /// Handles data for demoting a moderator of a chat channel
        /// </summary>
        /// <param name="session">Session making the request</param>
        /// <param name="packet">PlayerChatChannelPacket with information from client</param>
        public void HandleDemoteModeratorChatChannel(IClientSession session, PlayerChatChannelPacket packet)
        {

            if (!_channels.ContainsKey(packet.ChannelID))
            {
                //TODO: Reply with NotFound response
                return;
            }

            if (!_channels[packet.ChannelID].CanModifyModerator(session.SessionID))
            {
                //TODO: Reply with PermissionDenied response
                return;
            }

            //TODO Get UserID
            //TODO Verify user is not the user/owner

            _channels[packet.ChannelID].RemoveModerator(Guid.Empty, session.SessionID);

            //TODO Send success response to moderator
        }

        /// <summary>
        /// Handles data for setting the password of a chat channel
        /// </summary>
        /// <param name="session">Session making the request</param>
        /// <param name="packet">PasswordChatChannelPacket with information from client</param>
        public void HandleSetPasswordChatChannel(IClientSession session, PasswordChatChannelPacket packet)
        {

            if (!_channels.ContainsKey(packet.ChannelID))
            {
                //TODO: Reply with NotFound response
                return;
            }

            if (!_channels[packet.ChannelID].CanSetPassword(session.SessionID))
            {
                //TODO: Reply with PermissionDenied response
                return;
            }

            _channels[packet.ChannelID].SetChannelPassword(session.SessionID, packet.Password);

            //TODO: Reply with ChannelID Password Changed Response
        }

        /// <summary>
        /// Handles data for setting the owner of a chat channel
        /// </summary>
        /// <param name="session">Session making the request</param>
        /// <param name="packet">PlayerChatChannelPacket with information from client</param>
        public void HandleSetOwnerChatChannel(IClientSession session, PlayerChatChannelPacket packet)
        {

            if (!_channels.ContainsKey(packet.ChannelID))
            {
                //TODO: Reply with NotFound response
                return;
            }

            if (!_channels[packet.ChannelID].CanSetOwner(session.SessionID))
            {
                //TODO: Reply with PermissionDenied response
                return;
            }

            //Get UserID

            _channels[packet.ChannelID].SetChannelOwner(Guid.Empty, session.SessionID);

            //TODO: Reply with ChannelID Password Changed Response
        }

        /// <summary>
        /// Handles data for listing a player's subscribed channels
        /// </summary>
        /// <param name="session">Session making the request</param>
        public void HandleListChatChannel(IClientSession session)
        {
            //TODO: Reply with List Response
        }

    }
}